#include "ymodem.h"
#include "string.h"
#include "drv_flash.h"
#include <stdio.h>
#include <stdlib.h>
#include "drv_usart1.h"
#include "drv_timer.h"
#include "systemdata.h"
#include "bat_afe.h"

static uint8_t recvErrorTimes = 0;
static uint8_t handShakeFlag = 0;
static YmodemCtrlData_t ctrl_data = {0};
static Timer_t *timeout_timer = NULL;
	
static void printhex(uint8_t *data,uint16_t len){
	for(uint16_t i = 0;i<len;i++)
		printf("%02x ",data[i]);
	printf("\n");
}

void ymodem_send(uint8_t *data,uint16_t len,uint8_t comNum){
	printhex(data,len);
	usart4_send(data,len);
}

static inline uint16_t Ymodem_UpdateCRC16(uint16_t crcIn, uint8_t byte){
	uint32_t crc = crcIn;
	uint32_t in = byte|0x100;
	do{crc <<= 1;
		in <<= 1;
		if(in&0x100){
			++crc;
		}
		if(crc&0x10000){
			crc ^= 0x1021;
		}
	}while(!(in&0x10000));
	return (crc&0xffffu);
}

static inline uint16_t Ymodem_Cal_CRC16(const uint8_t* data, uint32_t size){
	uint32_t crc = 0;
	const uint8_t* dataEnd = data+size;
	while(data<dataEnd){
		crc = Ymodem_UpdateCRC16(crc,*data++);
	}
	crc = Ymodem_UpdateCRC16(crc,0);
	crc = Ymodem_UpdateCRC16(crc,0);
	return (crc&0xffffu);
}

static inline void timeout_callback(void *data){
	handShakeFlag = 1;
	ctrl_data.timeout_cnt ++;
	if(ctrl_data.timeout_cnt >= 10){
		ctrl_data.timeout_cnt = 0;
		ctrl_data.step = STEP_ERROR;
	}
}

static inline void ymodem_trans_init(void){
	memset(&ctrl_data,0,sizeof(YmodemCtrlData_t));
	if(timeout_timer == NULL){
		timeout_timer = timer_create(timeout_callback,500);
		timer_start(timeout_timer);
	}else{
		timer_start(timeout_timer);
	}
}

static inline void ymodem_parse_file_info(uint8_t *data){
	uint8_t fileName[FILE_NAME_LENGTH] = {0};
	uint8_t file_size_str[FILE_SIZE_LENGTH] = {0};
	memset(&fileName,0,FILE_NAME_LENGTH);
	memset(file_size_str,0,FILE_SIZE_LENGTH);
	uint8_t i=0;
	for(i=0;i<FILE_NAME_LENGTH;i++){
		if(*data != 0x00 != 0x00){
			fileName[i] = *(data++);
		}else{
			break;
		}
	}
	
	data ++;
	for(i=0;i<FILE_NAME_LENGTH;i++){
		if(*data != 0x00 && *data != 0x20){//space
			file_size_str[i] = *(data++);
		}else{
			ctrl_data.iap_size = atoi((char *)file_size_str);
			ctrl_data.total_pack_num = ctrl_data.iap_size/PACKET_1K_SIZE + (bool)(ctrl_data.iap_size%PACKET_1K_SIZE);
			ctrl_data.remain_size = ctrl_data.iap_size;				
			F460FLASH_Sector_Erase(DOWNLOAD_ADDR,ctrl_data.iap_size);				
			break;
		}
	}
}

static inline void write_file_infor(){
		F460FLASH_Sector_Erase(DOWNLOAD_FILEINFO_ADDR,8);
		uint8_t data[4] = {0};
		uint8_t i = 0;
		data[0] = ctrl_data.iap_size&0xff;
		data[1] = (ctrl_data.iap_size>>8)&0xff;
		data[2] = (ctrl_data.iap_size>>16)&0xff;
		data[3] = (ctrl_data.iap_size>>24)&0xff;
		F460FLASH_Write1(DOWNLOAD_FILEINFO_ADDR,data,4);
		uint8_t data2[4] = {'T','R','U','E'};
		F460FLASH_Write1(DOWNLOAD_FILEINFO_ADDR+4,data2,4);		
}

static inline void ymodem_write_flash(uint8_t *data,uint16_t len){
	if(ctrl_data.remain_size == 0){
		return;
	}
	//printf("write len = %d,iap_addr_offset = 0x%08x\n",len,ctrl_data.iap_addr_offset);
	F460FLASH_Write1(DOWNLOAD_ADDR + ctrl_data.iap_addr_offset,data,len);
	ctrl_data.iap_addr_offset += len;
	ctrl_data.remain_size -= len;
}

//包序号和crc校验检查，返回值：0、成功，1、失败
static inline uint8_t ymodem_crc_check_result(uint8_t *data){
	uint16_t pack_len = 0;
	if(data[0] == STX){
		pack_len = PACKET_1K_SIZE;
	}else{
		pack_len = PACKET_SIZE;
	}
	if(data[1] != ((~data[2])&0xff)){
		printf("Pack index error!\n");
		return 1;
	}
	uint16_t crcValue = Ymodem_Cal_CRC16(&data[PACKET_HEADER],pack_len);
	uint16_t recvCrc = data[pack_len + 3];
	recvCrc <<= 8;
	recvCrc |= data[pack_len + 4];
	if(crcValue != recvCrc){
		printf("crc check error!\n");
		return 1;
	}
	
	return 0;
}

static inline void ymodem_deal_soh(uint8_t *data){
	if(ctrl_data.step == STEP_HAND_SHAKE){
		ymodem_parse_file_info(&data[PACKET_HEADER]);
		ctrl_data.step = STEP_HAND_DONE;
	}else if(ctrl_data.step == STEP_PACK_DATA){
		if(ctrl_data.remain_size > PACKET_SIZE){
			ymodem_write_flash(&data[PACKET_HEADER],PACKET_SIZE);
		}else{
			ymodem_write_flash(&data[PACKET_HEADER],ctrl_data.remain_size);
		}
	}else if(ctrl_data.step == STEP_FINISH){
		write_file_infor();
		ctrl_data.step = STEP_FINISHED;
	}
}

static inline void ymodem_deal_stx(uint8_t *data){
	if(ctrl_data.remain_size > PACKET_1K_SIZE){
		ymodem_write_flash(&data[PACKET_HEADER],PACKET_1K_SIZE);
	}else{
		ymodem_write_flash(&data[PACKET_HEADER],ctrl_data.remain_size);
	}
}

static inline void ymodem_deal_eot(void){
	if(ctrl_data.step == STEP_PACK_DATA){
		ctrl_data.step = STEP_EOT_ONE;
	}else if(ctrl_data.step == STEP_EOT_ONE){
		ctrl_data.step = STEP_EOT_TWO;
	}else{
		printf("step error\n");
		ctrl_data.step = STEP_ERROR;
	}
}

static inline void ymodem_recv_process(uint8_t *data){
	
	if(data[0] == SOH || data[0] == STX){
		if(ymodem_crc_check_result(data) == 1){
			printf("Check error\n");
			ctrl_data.step = STEP_ERROR;
			return;
		}
		if(data[0] == SOH){//第一包、数据包小于128字节或最后一个结束包
			ymodem_deal_soh(data);
		}else{//1K数据包
			ymodem_deal_stx(data);
		}
	}else if(data[0] == EOT){//传输结束
		ymodem_deal_eot();
	}else{//不是协议数据
		printf("Not ymodem data!\n");
		ctrl_data.step = STEP_ERROR;
		return;
	}
}

static inline void ymodem_send_process(void){
	if(ctrl_data.step == STEP_HAND_SHAKE){//握手阶段，每500ms发送一次C
		if(handShakeFlag == 1){
			handShakeFlag = 0;
			uint8_t ch = CRC16;
			usart4_send(&ch,1);
		}
	}else if(ctrl_data.step == STEP_HAND_DONE){//握手完成，发送ACK和C，然后转数据传输阶段
		uint8_t buf[2] = {ACK,CRC16};
		usart4_send(buf,2);
		ctrl_data.step = STEP_PACK_DATA;
	}else if(ctrl_data.step == STEP_PACK_DATA){
		uint8_t ch = ACK;
		if(ctrl_data.recv_data_flag == 1){//如果收到数据，立即回复ACK
			usart4_send(&ch,1);
		}
	}else if(ctrl_data.step == STEP_EOT_ONE){
		uint8_t ch = NAK;
		if(ctrl_data.recv_data_flag == 1){//如果收到数据，立即回复NAK
			usart4_send(&ch,1);
		}
	}else if(ctrl_data.step == STEP_EOT_TWO){
		uint8_t ch = CRC16;
		usart4_send(&ch,1);
		ctrl_data.step = STEP_FINISH;
	}else if(ctrl_data.step == STEP_FINISHED){
		uint8_t ch = ACK;
		usart4_send(&ch,1);
	}
}

void ymodem_update_process(void){
	if(ctrl_data.step == STEP_START){
		ymodem_trans_init();
		ctrl_data.step = STEP_HAND_SHAKE;
	}
	UsartRxInfo_t *rx_info4 = get_uart4_info();
	if(rx_info4->recv_ok_flag == 1){
		// for(uint16_t i = 0;i<rx_info4->recv_len;i++){
		// 	printf("%02x ",rx_info4->recv_buf[i]);
		// }
		// printf("\n");
		ymodem_recv_process(rx_info4->recv_buf);
		ctrl_data.recv_data_flag = 1;
		ctrl_data.timeout_cnt = 0;
		rx_info4->recv_len = 0;
		rx_info4->recv_ok_flag = 0;
	}else{
		ctrl_data.recv_data_flag = 0;
	}
	ymodem_send_process();
	if(ctrl_data.step == STEP_ERROR || ctrl_data.step == STEP_FINISHED){
		if(ctrl_data.step == STEP_FINISHED){//如果是升级成功，则重启
			afe_chg_mos_ctrl(false);
			afe_dsg_mos_ctrl(false);
			Soft_Reset();
		}
		ctrl_data.step = STEP_START;
		SysRunData_t *runinfo = getSysRunData();
		runinfo->sysSwState.bits.SW_OTA = 0;//退出升级流程
		timer_stop(timeout_timer);
		memset(&ctrl_data,0,sizeof(ctrl_data));
	}
}